package com.wanlian.dao.impl;

import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.wanlian.dao.GenericDao;
import com.wanlian.vo.Page;
import org.springframework.jdbc.core.JdbcTemplate;

public abstract class GenericDaoImpl<T> implements GenericDao<T> {

    public final String HQL = "hql";

    public final String PARAMS = "params";

    private Class<T> entityClass;

    public GenericDaoImpl(Class<T> clazz) {
        this.entityClass = clazz;
    }

    /**
     * 使用注解方式，注入SessionFactory
     */
    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public Session currentSession() {
        return this.sessionFactory.getCurrentSession();
    }

    protected String entityClassName() {
        return this.entityClass.getSimpleName();
    }

    protected String entityClassFullName() {
        return this.entityClass.getName();
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    // 增加或者修改一条记录
    @Override
    public void insert(T t) {
        this.currentSession().saveOrUpdate(t);
    }

    @Override
    public void add(List<T> list) {
        for (T t : list) {
            this.currentSession().save(t);
        }
    }

    // 增加一条记录
    @Override
    public void add(T t) {
        this.currentSession().save(t);
    }

    // 按对象删除一条记录
    @Override
    public void delete(T t) {
        this.currentSession().delete(t);
    }

    // 以字符串id删除一条记录
    @Override
    public void delete(String id) {

        String hql = "delete from " + this.entityClassName() + " where id = '" + id + "'";

        this.currentSession().createCriteria(hql);

        T t = (T) queryById(id);

        if (t != null) {
            delete(t);
        }
    }

    // 以整形id删除一条记录
    @Override
    public void delete(int id) {

        String hql = "delete from " + this.entityClassName() + " where id = " + id;

        this.currentSession().createCriteria(hql);

        T t = (T) queryById(id);

        if (t != null) {
            delete(t);
        }
    }

    // 更新一条记录
    @Override
    public void update(T t) {
        //this.currentSession().clear();
        this.currentSession().update(t);
    }

    // 更新一条记录
    @Override
    public void updateByMerge(T t) {
        this.currentSession().merge(t);
    }

    // 以字符串id查询一条记录
    @Override
    public T queryById(String id) {
        return (T) this.currentSession().get(entityClass, id);
    }


    // 以整形id查询一条记录
    @Override
    public T queryById(int id) {
        return (T) this.currentSession().get(entityClass, id);
    }

    // 以长整形Long查询一条记录
    @Override
    public T queryById(Long id) {
        return (T) this.currentSession().get(entityClass, id);
    }

    // 查询所有记录，以List的形式返回所有的查询记录
    @Override
    public List<T> queryAll() {
        String hql = "from " + entityClass.getSimpleName();
        return queryForList(hql, null);
    }

    // 实现分页查询，将页面所需记录以list形式返回
    @Override
    public List<T> queryAllByPage(Page page) {
        String hql = "from " + this.entityClassName();
        return queryForListByPage(hql, null, page);
    }

    // 按条件查询，返回唯一结果，需要根据具体业务自己写条件查询的hql语句，
    // params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    @SuppressWarnings("unchecked")
    @Override
    public T queryForObject(String hql, Object[] params) {
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, params);
        return (T) query.uniqueResult();
    }

    // 按条件查询，返回查询结果的第一条记录，
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    @Override
    public T queryForTopObject(String hql, Object[] params) {
        List<T> list = queryForList(hql, params, 0);
        if (list != null && list.size() != 0) {
            return list.get(0);
        } else {
            return null;
        }
    }

    // 按条件查询，并以List的形式返回所有的查询结果
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    @Override
    public List<T> queryForList(String hql, Object[] params) {
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, params);
        return query.list();
    }

    // 按条件查询，并以List的形式返回所有的查询结果
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    @Override
    public List<Map<String, Object>> querySQLForList(String hql, Object[] params, String[] columns) {
        SQLQuery query = this.currentSession().createSQLQuery(hql);
        setQueryParams(query, params);
        List<Object[]> querylist = query.list();
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        for (Object[] obj : querylist) {
            Map<String, Object> map = new HashMap<String, Object>();
            for (int i = 0; i < columns.length; i++) {
                map.put(columns[i], obj[i]);
            }
            list.add(map);
        }
        return list;
    }

    /**
     * 通过条件查询
     */
    public List<T> queryForListByWhere(String wherehql, Object[] params) {
        String hql = String.format("from %s where   %s", entityClassName(), wherehql);
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, params);
        return query.list();
    }

    // 按条件查询，并取得结果个数
    @Override
    public Double queryDCount(String hql, Object[] params) {
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, params);
        return (Double) query.list().get(0);
    }

    // 按条件查询，并取得结果个数
    @Override
    public Long queryCount(String hql, Object[] params) {
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, params);
        return (Long) query.list().get(0);
    }

    public long querySQLForCount(String sql, Object[] params) {
        SQLQuery query = this.currentSession().createSQLQuery(sql);
        setQueryParams(query, params);
        BigInteger icount = (BigInteger) query.list().get(0);
        return icount.longValue();
    }

    // 按条件查询，以分页的形式返回查询结果List<T>,需要传入page参数
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    // 添加注释以便与下文的queryPageByMapConditions区分： 主要支持固定多条件查询，但sql语句应该在service层添加
    @Override
    public List<T> queryForListByPage(String hql, Object[] params, Page page) {
        int num = (page.getPage() - 1) * page.getNum();
        Query query = this.currentSession().createQuery(hql);
        query.setFirstResult(num);
        query.setMaxResults(page.getNum());
        setQueryParams(query, params);
        return query.list();
    }

    @Override
    public List<Object> queryForListObjectByPage(String hql, Object[] params, Page page) {
        SQLQuery query = this.currentSession().createSQLQuery(hql);
        if(page!=null) {
            int num = (page.getPage() - 1) * page.getNum();
            query.setFirstResult(num);
            query.setMaxResults(page.getNum());
        }
        setQueryParams(query, params);
        List<Object[]> querylist = query.list();
        return query.list();
    }

    // 按条件查询，以分页的形式返回查询结果List<T>,需要传入page参数
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    // 添加注释以便与下文的queryPageByMapConditions区分： 主要支持固定多条件查询，但sql语句应该在service层添加
    @Override
    public List<T> querySQLForListByPage(String hql, Object[] params, Page page) {
        int num = (page.getPage() - 1) * page.getNum();
        SQLQuery query = this.currentSession().createSQLQuery(hql);
        query.addEntity(this.entityClassFullName());
        query.setFirstResult(num);
        query.setMaxResults(page.getNum());
        setQueryParams(query, params);
        return query.list();
    }

    // 按条件查询，返回recordNum及其以后的所有记录；
    // 需要根据具体业务自己写条件查询的hql语句，params为由查询条件拼接的数组，注意数组中的元素要和hql语句中的？对应，
    // 长度和位置均需保持一致
    @Override
    public List<T> queryForList(final String hql, final Object[] params, final int recordNum) {
        Query query = this.currentSession().createQuery(hql);
        query.setFirstResult(recordNum);
        setQueryParams(query, params);

        return query.list();
    }

    // 将数组params中的参数set进query查询对象中
    public void setQueryParams(Query query, Object[] params) {
        if (null == params || params.length == 0) {
            return;
        }

        for (int i = 0; i < params.length; i++) {
            query.setParameter(i, params[i]);
        }
    }

    private void setQueryParams(Query query, Map<String, Object> map) {
        if (map != null) {
            Set<String> keySet = map.keySet();
            for (String string : keySet) {
                Object obj = map.get(string);
                // 这里考虑传入的参数是什么类型，不同类型使用的方法不同
                if (obj instanceof Collection<?>) {
                    query.setParameterList(string, (Collection<?>) obj);
                } else if (obj instanceof Object[]) {
                    query.setParameterList(string, (Object[]) obj);
                } else {
                    query.setParameter(string, obj);
                }
            }
        }
    }

    // 按条件查询，条件以Map的形式传到后台,以分页的形式返回数据，
    // 注意只能进行完成简单的条件查询的hql语句的拼接
    @Override
    public List<T> queryPageByMapConditions(Page page, Map<String, Object> queryConditions) {
        int num = (page.getPage() - 1) * page.getNum();
        StringBuffer hql = new StringBuffer("from " + this.entityClassName() + " where 1=1 ");
        // 遍历查询封装条件的map，拼接hql语句
        for (String key : queryConditions.keySet()) {
            if (queryConditions.get(key) instanceof List) {
                hql.append(" and " + key + " in (:" + queryConditions.get(key) + ")");
            } else {
                hql.append(" and " + key + "=:" + queryConditions.get(key));
            }
        }

        System.out.println(hql);
        Query query = this.currentSession().createQuery(hql.toString());

        for (Entry<String, Object> entry : queryConditions.entrySet()) {
            if (entry.getValue() instanceof List) {
                query.setParameterList(entry.getKey(), (List) entry.getValue());
            } else {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        query.setFirstResult(num);
        query.setMaxResults(page.getNum());
        return query.list();
    }

    // 以整形id删除一条记录
    @Override
    public void executeHql(String hql, Map<String, Object> map) {
        Query query = this.currentSession().createQuery(hql);
        //setQueryParams(query, map);
        query.executeUpdate();
    }

    // 以整形id删除一条记录
    @Override
    public int executeHqlWithCount(String hql, Map<String, Object> map) {
        Query query = this.currentSession().createQuery(hql);
        setQueryParams(query, map);
        return query.executeUpdate();
    }

    @Override
    public void executeSql(String sql) {
        SQLQuery query = this.currentSession().createSQLQuery(sql);
        query.executeUpdate();
    }

    @Override
    public List<Map<String, Object>> queryForListObjectByPage(String hql, Object[] params, Page page, String[] column) {
        int num = (page.getPage() - 1) * page.getNum();
        SQLQuery query = this.currentSession().createSQLQuery(hql);
        query.setFirstResult(num);
        query.setMaxResults(page.getNum());
        setQueryParams(query, params);
        List<Object[]> querylist = query.list();
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        for (Object[] obj : querylist) {
            Map<String, Object> map = new HashMap<String, Object>();
            for (int i = 0; i < column.length; i++) {
                map.put(column[i], obj[i]);
            }
            list.add(map);
        }
        return list;
    }

    @Override
    public List<Object[]> queryForListCu(String hql, Object[] params) {
        SQLQuery query = this.currentSession().createSQLQuery(hql);
        setQueryParams(query, params);
        List<Object[]> querylist = query.list();
        return querylist;
    }

    @Override
    public int queryCountByParam(String hql, Object[] params) {
        Query query = this.currentSession().createSQLQuery(hql);
        setQueryParams(query, params);
        return Integer.parseInt(query.list().get(0) + "");
    }

    @Override
    public int queryRowNumByParam(String hql, Object[] params) {
        Query query = this.currentSession().createSQLQuery(hql);
        setQueryParams(query, params);
        return query.list().size();
    }

    @Override
    public int updateByJdbc(String sql, Object[] params) {
        return jdbcTemplate.update(sql, params);
    }

    @Override
    public int updateBatchByJdbc(String sql, List<Object[]> params) {
        return jdbcTemplate.batchUpdate(sql, params).length;
    }
}
